<?php


namespace tmcore\utils;

use think\helper\Str;
use tmcore\traits\ErrorTrait;

class Helper
{
    use ErrorTrait;

    /**
     * 检验手机号
     *
     * @param string $mobile
     * @param bool $is_strict
     * @return bool
     */
     public static function check_mobile($mobile, $is_strict = false) {
         if (strlen($mobile) == "11") {
             if($is_strict) {
                 if (!preg_match("/^1[3456789]\d{9}$/", $mobile)) return false;
             }
         } else {
             return false;
         }
         return true;
     }

    /**
     * 检验邮箱
     *
     * @param string $email 邮箱
     * @return bool
     */
    public static function check_email($email) {
        if (!preg_match('/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/', $email)) {
            return false;
        }
        //使用 FILTER_VALIDATE_EMAIL 过滤器
        if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return false;
        }
        return true;
    }

    /**
     * 检验网址
     *
     * @param string $url 网址
     * @return bool
     */
    public static function check_url($url) {
        $url = strtolower(trim($url));
        if(empty($url)) {
            return false;
        }
        $match = '/^(http:\/\/)?(https:\/\/)?([\w\d\-]+\.)+[\w\-]+(\/[\d\w\-.\/?%&=]*)?$/';
        if (!preg_match($match, $url)) {
            return false;
        }
        return true;
    }

    /**
     * 检验名称
     *
     * @param string $name 名称
     * @param string $regular 正则
     * @return bool
     */
    public static function check_name($name, $regular = '') {
        if(empty($name)) {
            return false;
        }
        //6-20位字符，只能包含数字、字母、下划线
        $match = $regular?$regular:'/^[a-zA-Z0-9-_]{6,20}$/';
        if (!preg_match($match, $name)) {
            return false;
        }
        return true;
    }

    /**
     * 获取随机数
     * @param int $length
     * @param string $chars
     * @return string
     */
    public static function get_random($length = 16, $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
    {
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * uri小写
     * @param $data
     * @return array|string[]
     */
    public static function lower_uri($data)
    {
        if (!is_array($data)) {
            $data = [$data];
        }
        return array_map(function ($item) {
            return strtolower(Str::camel($item));
        }, $data);
    }

    /**
     * 检验密码
     *
     * @param string $pwd 密码
     * @param string $regular 正则
     * @return bool
     */
    public static function check_password($pwd, $regular = '') {
        if(empty($pwd)) {
            return false;
        }
        // 6-16位，同时包含字母、数字、特殊符号
        $match = $regular?$regular:'/^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:;\'&lt;>?,.\/]).{6,16}$/';

        if (!preg_match($match, $pwd)) {
            return false;
        }
        return true;
    }

    /**
     * 检验ip
     *
     * @param string $ip 网址
     * @return bool
     */
    public static function check_ip($ip) {
        if(empty($ip)) {
            return false;
        }
        $match = '/^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$/';
        if (!preg_match($match, $ip)) {
            return false;
        }
        return true;
    }

    /**
     * 检验电话号
     *
     * @param string $phone 电话号
     * @return bool
     */
    public static function check_phone($phone) {
        $match = '/^0[0-9]{2,3}[-]?\d{7,8}$/';
        if (!preg_match($match, $phone)) {
            return false;
        }
        return true;
    }

    /**
     * 检验身份证号
     *
     * @param string $idcard 身份证号
     * @return bool
     */
    public static function check_id_number($idcard)
    {
        if(empty($idcard)){
            return self::setError('身份证不可为空');
        }else{
            $idcard = strtoupper($idcard); # 如果是小写x,转化为大写X
            if(strlen($idcard) != 18 && strlen($idcard) != 15){
                return self::setError('身份证位数不符');
            }
            # 如果是15位身份证，则转化为18位
            if(strlen($idcard) == 15){
                # 如果身份证顺序码是996 997 998 999，这些是为百岁以上老人的特殊编码
                if (array_search(substr($idcard, 12, 3), array('996', '997', '998', '999')) !== false) {
                    $idcard = substr($idcard, 0, 6) . '18' . substr($idcard, 6, 9);
                } else {
                    $idcard = substr($idcard, 0, 6) . '19' . substr($idcard, 6, 9);
                }
                # 加权因子
                $factor = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
                # 校验码对应值
                $code = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
                $checksum = 0;
                for ($i = 0; $i < strlen($idcard); $i++) {
                    $checksum += substr($idcard, $i, 1) * $factor[$i];
                }
                $idcard = $idcard . $code[$checksum % 11];
            }
            # 验证身份证开始
            $IDCardBody = substr($idcard, 0, 17); # 身份证主体
            $IDCardCode = strtoupper(substr($idcard, 17, 1)); # 身份证最后一位的验证码

            # 加权因子
            $factor = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
            # 校验码对应值
            $code = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
            $checksum = 0;
            for ($i = 0; $i < strlen($IDCardBody); $i++) {
                $checksum += substr($IDCardBody, $i, 1) * $factor[$i];
            }
            $validateIdcard = $code[$checksum % 11];    # 判断身份证是否合理
            if($validateIdcard != $IDCardCode){
                return self::setError('身份证有误');
            }else{
                return true;
            }
        }
    }


    /**
     * 图片转Base64
     *
     * @author windy
     * @param string $path (文件绝对路径)
     * @return string
     */
    public static function image_to_base64(string $path): string
    {
        if($fp = fopen($path,"rb", 0)) {
            $gambit = fread($fp, filesize($path));
            fclose($fp);
            $base64 = chunk_split(base64_encode($gambit));
            return 'data:image/png;base64,' . $base64;
        }
        return '';
    }

    /**
     * 隐藏部分文字
     *
     * @author windy
     * @param $str
     * @return string
     */
    public static function hideStr($str): string
    {
        if (mb_strlen($str) >= 3) {
            return '**' . mb_substr($str, 2);
        }
        if (mb_strlen($str) == 1) {
            return '**' . $str;
        }
        if (mb_strlen($str) == 2) {
            return '**' . mb_substr($str, 1);
        }
        return $str;
    }

    /**
     * 隐藏手机号中间四位
     *
     * @author windy
     * @param $str
     * @return string
     */
    public static function hideMobile($str): string
    {
        return substr_replace($str, '****', 3, 4);
    }

    /**
     * 从网络下载资源
     * @param string $url 下载资源的url
     * @param string $savePath 文件存储路径
     * @return bool
     */
    public static function download_net_resource($url, $savePath)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
        $file = curl_exec($ch);
        curl_close($ch);
        $resource = fopen($savePath, 'a');
        $res = fwrite($resource, $file);
        fclose($resource);

        if ($res !== false && $file) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 去除表情符号
     * @param $str
     * @return string|string[]|null
     */
    public static function filter_emoji($str)
    {
        $str = preg_replace_callback(    //执行一个正则表达式搜索并且使用一个回调进行替换
            '/./u',
            function (array $match) {
                return strlen($match[0]) >= 4 ? '' : $match[0];
            },
            $str);
        return $str;
    }

    /**
     * 获取字符串中英文混合长度
     * @param string $str 字符串 1中文=1位，2英文=1位
     * @param string $charset 编码 utf-8-默认 gb2312
     * @return false|float
     */
    public static function get_str_length($str, $charset = 'utf-8')
    {
        if ($charset == 'utf-8') {
            $str = iconv('utf-8', 'gb2312', $str);
        }
        $num = strlen($str);
        $cnNum = 0;
        for ($i = 0; $i < $num; $i++) {
            if (ord(substr($str, $i + 1, 1)) > 127) {
                $cnNum++;
                $i++;
            }
        }
        $enNum = $num - ($cnNum * 2);
        $number = ($enNum / 2) + $cnNum;
        return ceil($number);
    }


    /**
     * 下载文件
     *
     * @author windy
     * @param String $fileUrl (远程文件路径)
     * @param String $saveTo (保存地址)
     * @param String $ext (保存后的扩展名)
     * @return string 保存后的文件路径
     */
    public static function download_file(String $fileUrl, String $saveTo, String $ext): string
    {
        if (!$fileUrl) {
            return '';
        }

        if (!file_exists($saveTo)) {
            mkdir($saveTo, 0775, true);
        }

        $content = file_get_contents($fileUrl);
        file_put_contents($saveTo . md5($fileUrl) . '.' . $ext, $content);
        return $saveTo . md5($fileUrl) . '.' . $ext;
    }

    /**
     * 防止提交内容(sql)注入攻击代码
     * @param $val
     * @return bool true-无风险 false-有风险
     */
    public static function remove_xss($val)
    {
        $original = $val;
        // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
        // this prevents some character re-spacing such as <java\0script>
        // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs
        $val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
        // straight replacements, the user should never need these since they're normal characters
        // this prevents like <IMG SRC=@avascript:alert('XSS')>
        $search = 'abcdefghijklmnopqrstuvwxyz';
        $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $search .= '1234567890!@#$%^&*()';
        $search .= '~`";:?+/={}[]-_|\'\\';
        for ($i = 0; $i < strlen($search); $i++) {
            // ;? matches the ;, which is optional
            // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars
            // @ @ search for the hex values
            $val = preg_replace('/(&#[xX]0{0,8}' . dechex(ord($search[$i])) . ';?)/i', $search[$i], $val); // with a ;
            // @ @ 0{0,7} matches '0' zero to seven times
            $val = preg_replace('/(&#0{0,8}' . ord($search[$i]) . ';?)/', $search[$i], $val); // with a ;
        }
        // now the only remaining whitespace attacks are \t, \n, and \r
        $ra1 = array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
        $ra2 = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
        $ra = array_merge($ra1, $ra2);
        $found = true; // keep replacing as long as the previous round replaced something
        while ($found == true) {
            $val_before = $val;
            for ($i = 0; $i < sizeof($ra); $i++) {
                $pattern = '/';
                for ($j = 0; $j < strlen($ra[$i]); $j++) {
                    if ($j > 0) {
                        $pattern .= '(';
                        $pattern .= '(&#[xX]0{0,8}([9ab]);)';
                        $pattern .= '|';
                        $pattern .= '|(&#0{0,8}([9|10|13]);)';
                        $pattern .= ')*';
                    }
                    $pattern .= $ra[$i][$j];
                }
                $pattern .= '/i';
                $replacement = substr($ra[$i], 0, 2) . '<x>' . substr($ra[$i], 2); // add in <> to nerf the tag
                $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
                if ($val_before == $val) {
                    // no replacements were made, so exit the loop
                    $found = false;
                }
            }
        }
        $ultimately = $val;
        if ($ultimately == $original) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 过滤html
     *
     * @param $str
     * @return string
     */
    public static function filter_html($str)
    {
        $str = preg_replace("/<\s*img\s+[^>]*?src\s*=\s*(\'|\")(.*?)\\1[^>]*?\/?\s*>/i", " ", $str); //过滤img标签
        $str = preg_replace("/\s+/", " ", $str); //过滤多余回车
        $str = preg_replace("/<[ ]+/si", "<", $str); //过滤<__("<"号后面带空格)
        $str = preg_replace("/<\!--.*?-->/si", "", $str); //注释
        $str = preg_replace("/<(\!.*?)>/si", "", $str); //过滤DOCTYPE
        $str = preg_replace("/<(\/?html.*?)>/si", "", $str); //过滤html标签
        $str = preg_replace("/<(\/?head.*?)>/si", "", $str); //过滤head标签
        $str = preg_replace("/<(\/?meta.*?)>/si", "", $str); //过滤meta标签
        $str = preg_replace("/<(\/?body.*?)>/si", "", $str); //过滤body标签
        $str = preg_replace("/<(\/?link.*?)>/si", "", $str); //过滤link标签
        $str = preg_replace("/<(\/?form.*?)>/si", "", $str); //过滤form标签
        $str = preg_replace("/cookie/si", "COOKIE", $str); //过滤COOKIE标签
        $str = preg_replace("/<(applet.*?)>(.*?)<(\/applet.*?)>/si", "", $str); //过滤applet标签
        $str = preg_replace("/<(\/?applet.*?)>/si", "", $str); //过滤applet标签
        $str = preg_replace("/<(style.*?)>(.*?)<(\/style.*?)>/si", "", $str); //过滤style标签
        $str = preg_replace("/<(\/?style.*?)>/si", "", $str); //过滤style标签
        $str = preg_replace("/<(title.*?)>(.*?)<(\/title.*?)>/si", "", $str); //过滤title标签
        $str = preg_replace("/<(\/?title.*?)>/si", "", $str); //过滤title标签
        $str = preg_replace("/<(object.*?)>(.*?)<(\/object.*?)>/si", "", $str); //过滤object标签
        $str = preg_replace("/<(\/?objec.*?)>/si", "", $str); //过滤object标签
        $str = preg_replace("/<(noframes.*?)>(.*?)<(\/noframes.*?)>/si", "", $str); //过滤noframes标签
        $str = preg_replace("/<(\/?noframes.*?)>/si", "", $str); //过滤noframes标签
        $str = preg_replace("/<(i?frame.*?)>(.*?)<(\/i?frame.*?)>/si", "", $str); //过滤frame标签
        $str = preg_replace("/<(\/?i?frame.*?)>/si", "", $str); //过滤frame标签
        $str = preg_replace("/<(script.*?)>(.*?)<(\/script.*?)>/si", "", $str); //过滤script标签
        $str = preg_replace("/<(\/?script.*?)>/si", "", $str); //过滤script标签
        $str = preg_replace("/javascript/si", "Javascript", $str); //过滤script标签
        $str = preg_replace("/vbscript/si", "Vbscript", $str); //过滤script标签
        $str = preg_replace("/on([a-z]+)\s*=/si", "On\\1=", $str); //过滤script标签
        $str = preg_replace("/&#/si", "&＃", $str); //过滤script标签

        return $str;
    }

    /**
     * 版本号的比较
     *
     * @param string $version1 版本号
     * @param string $version2 版本号
     * @return bool|int 0-两版本相等 -1-小于第二个 1-大于第二个
     */
    public static function version_code_compare($version1, $version2)
    {
        $version1_arr = explode(".", $version1);
        $version2_arr = explode(".", $version2);
        $max_length = max(count($version1_arr), count($version2_arr));
        $version1_arr = array_pad($version1_arr, $max_length, 0);
        $version2_arr = array_pad($version2_arr, $max_length, 0);

        return version_compare(implode(".", $version1_arr), implode(".", $version2_arr));
    }

    /**
     * 获取图片后缀
     *
     * @param string $file_path
     * @return string
     */
    public static function get_image_type($file_path)
    {
        $file = fopen($file_path, 'rb');
        $bin = fread($file, 2); //只读2字节
        fclose($file);
        $strInfo = @unpack('C2chars', $bin);
        $typeCode = intval($strInfo['chars1'] . $strInfo['chars2']);
        switch ($typeCode) {
            case 255216:
                $fileType = 'jpg';
                break;
            case 7173:
                $fileType = 'gif';
                break;
            case 6677:
                $fileType = 'bmp';
                break;
            case 13780:
                $fileType = 'png';
                break;
            default:
                $fileType = '';
        }
        return $fileType;
    }

    /**
     * url base64编码
     * @param string $url
     * @return string
     */
    public static function url_b64encode($url)
    {
        $data = base64_encode($url);
        return str_replace(array('+', '/', '='), array('-', '_', ''), $data);
    }

    /**
     * url base64解码
     * @param string $url
     * @return string
     */
    public static function url_b64decode($url)
    {
        $data = str_replace(array('-', '_'), array('+', '/'), $url);
        $mod4 = strlen($data) % 4;
        if ($mod4) {
            $data .= substr('====', $mod4);
        }
        return base64_decode($data);
    }

    /**
     * 文件单位转换
     * @param string $size 文件字节数
     * @param string $format 单位名称 kb mb gb
     * @return string
     */
    public static function format_bytes($size, $format)
    {
        $p = 0;
        if ($format == 'kb') {
            $p = 1;
        } elseif ($format == 'mb') {
            $p = 2;
        } elseif ($format == 'gb') {
            $p = 3;
        }
        $size /= pow(1024, $p);
        return number_format($size, 3);
    }

    /**
     * 将xml转为数组
     * @param string $xml
     * @return mixed
     */
    public static function xml_to_array($xml)
    {
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    /**
     * 数组转xml
     * @param $data
     * @return bool|string
     */
    public static function array_to_xml($data)
    {
        if (!is_array($data) || count($data) <= 0) {
            return false;
        }
        $xml = "<xml>";
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * 检验是否是xml
     * @param string $str 字符串
     * @return bool
     */
    public static function is_xml($str)
    {
        $xml_parser = xml_parser_create();

        if (!xml_parse($xml_parser, $str, true)) {
            xml_parser_free($xml_parser);
            return false;
        } else {
            return true;
        }
    }


    /**
     * 检验路径是否存在，不存在则创建
     * @param $path
     * @param $mode
     * @return void
     */
    public static function check_mkdir($path, $mode = 0755)
    {
        if (!is_dir(dirname($path))) {
            mkdir(dirname($path), $mode, true);
        }
    }
    /**
     * 获取身份证号年龄
     * @param string $id 身份证号
     * @return int
     */
    public static function get_id_number_age($id)
    {
        if (empty($id)) return 0;
        $date = strtotime(substr($id, 6, 8));
        $today = strtotime('today');
        $diff = floor(($today - $date) / 86400 / 365);
        return strtotime(substr($id, 6, 8) . ' +' . $diff . 'years') > $today ? ($diff + 1) : $diff;
    }

    /**
     * 格式化URL
     * @param array $data
     * @return array|string[]
     */
    public static function formatUrl(array $data)
    {
        return array_map(function ($item) {
            return strtolower(Str::camel($item));
        }, $data);
    }


    /**
     * 打印文本日志 (换行追加)
     * @param string $path 相对路径
     * @param array $data 打印数据
     * 打印示例：Helper::printTextLog('runtime/log.txt', ['time' => Time::msToTime(Time::millisecond())])
     * @return false|int 失败：false,成功：字节数
     */
    public static function printTextLog(string $path, array $data)
    {
        return file_put_contents(app()->getRootPath() . $path,json_encode($data, 320) . PHP_EOL,FILE_APPEND);
    }
}